﻿#pragma once

#include "Framework/Application.h"
#include "Output/Console.h"
#include "Math/Math.h"

#include <glm/gtc/quaternion.hpp>
#include <glm/ext/matrix_transform.hpp>

#include "Path/Paths.h"
#include "Object/NewObject.h"

CString ToString(const glm::quat& inQutnion)
{
   return U"glm::Quat[x:"+CString(inQutnion.x)
            +U",y:"+CString(inQutnion.y)
            +U",z:"+CString(inQutnion.z)
            +U",w:"+CString(inQutnion.w);
}

CString ToString(const glm::mat3x3& Data)
{
    return U"glm::Matrix3x3:[\n"
    +CString(Data[0][0])+U","+CString(Data[1][0])+U","+CString(Data[2][0])+U"\n"
    +CString(Data[0][1])+U","+CString(Data[1][1])+U","+CString(Data[2][1])+U"\n"
    +CString(Data[0][2])+U","+CString(Data[1][2])+U","+CString(Data[2][2])+U"\n"
    +U"]";
}

CString ToString(const glm::mat4x4& Data)
{
    return U"glm::Matrix4x4:[\n"
    +CString(Data[0][0])+U","+CString(Data[1][0])+U","+CString(Data[2][0])+U","+CString(Data[3][0])+U"\n"
    +CString(Data[0][1])+U","+CString(Data[1][1])+U","+CString(Data[2][1])+U","+CString(Data[3][1])+U"\n"
    +CString(Data[0][2])+U","+CString(Data[1][2])+U","+CString(Data[2][2])+U","+CString(Data[3][2])+U"\n"
    +CString(Data[0][3])+U","+CString(Data[1][3])+U","+CString(Data[2][3])+U","+CString(Data[3][3])+U"\n"
    +U"]";
}
CString ToString(const glm::vec3& Data)
{
    return U"glm::vec3:["
    +CString(Data.x)+U","
    +CString(Data.y)+U","
    +CString(Data.z)
    +U"]";
}

void TestPath()
{
  WH::PrintLine(U"CWD:"+SPaths::GetWorkingDirectory().ToString());
  WH::PrintLine(U"ExecutablePath:"+SPaths::GetExecutablePath().ToString());
}

void TestBasic()
{
   WH::PrintLine(U"Test Basic:");
   WH::PrintLine(CString(CMathBase::Clamp(-222,std::numeric_limits<float>::lowest(),std::numeric_limits<float>::max())));


}

//implement SDL_main here
int main(int argc, char* argv[])
{
  auto App=NewObject<RApplication>();

  SApplicationArgument Arguments;
  Arguments.ExecutablePath=SPath(argv[0]);
  App->Initialize(Arguments);
   
   //Test Math;
   WH::PrintLine(U"TestMath");
   {
      //test ToEuler
      {

        glm::quat GlmQuat=glm::quat(glm::radians(glm::vec3(30.0f, 40.0f, 50.0f))); 
        SQuat Quat=SQuat::FromEuler(SVec3(CMathBase::DegreeToRadian(30.0f), CMathBase::DegreeToRadian(40.0f), CMathBase::DegreeToRadian(50.0f)));

        WH::PrintLine(ToString(GlmQuat));
        WH::PrintLine(Quat.ToString());

        glm::mat3x3 GlmMatrix=glm::mat3_cast(GlmQuat);
        SMatrix3x3 Matrix =SMatrix3x3::FromEuler(SVec3(CMathBase::DegreeToRadian(30.0f), CMathBase::DegreeToRadian(40.0f), CMathBase::DegreeToRadian(50.0f)));
        
        WH::PrintLine(ToString(GlmMatrix));
        WH::PrintLine(Matrix.ToString());        


        WH::PrintLine(ToString(glm::eulerAngles(GlmQuat)));
        WH::PrintLine(Quat.ToEuler().ToString());

      }

      glm::quat GlmQuat=glm::quat(glm::radians(glm::vec3(30.0f, 0.0f, 0.0f)));  //guess it is [1,0,0,0]
      glm::quat GlmQuat2=glm::quat(glm::radians(glm::vec3(0.0f, 200.0f, 0.0f)));  //guess it is [1,0,0,0]   

      auto GlmQuat3 =glm::slerp(GlmQuat,GlmQuat2,0.5f);

      SQuat Quat(SVec3(1,0,0),CMathBase::DegreeToRadian(30.f));
      SQuat Quat2(SVec3(0,1,0),CMathBase::DegreeToRadian(200.f));

      SQuat Quat3= Slerp(Quat,Quat2,0.5f);

      WH::PrintLine(ToString(GlmQuat3));
      WH::PrintLine(Quat3.ToString());

      WH::PrintLine(ToString(GlmQuat3*GlmQuat3));
      WH::PrintLine((Quat3*Quat3).ToString());      

      glm::mat3x3 GlmMatrix3=glm::mat3_cast(GlmQuat3);
      SMatrix3x3 Matrix3(Quat3);

      WH::PrintLine(ToString(GlmMatrix3));
      WH::PrintLine(Matrix3.ToString());

      WH::PrintLine(ToString(GlmMatrix3*GlmMatrix3));
      WH::PrintLine((Matrix3*Matrix3).ToString());

      WH::PrintLine(ToString(glm::quat_cast(GlmMatrix3*GlmMatrix3)));
      WH::PrintLine((Matrix3*Matrix3).ToQuat().ToString());   


      WH::PrintLine(ToString(glm::quat_cast(GlmMatrix3)));
      WH::PrintLine((Matrix3).ToQuat().ToString());   


      glm::mat4x4 GlmMatrix4(1); //create a identity
      GlmMatrix4=glm::rotate(GlmMatrix4,glm::angle(GlmQuat3),glm::axis(GlmQuat3));
      SMatrix4x4 Matrix4(SVec3(0,0,0),Matrix3);

      WH::PrintLine(ToString(GlmMatrix4));
      WH::PrintLine(Matrix4.ToString());

      WH::PrintLine(ToString(GlmMatrix4*GlmMatrix4));
      WH::PrintLine((Matrix4*Matrix4).ToString());

      glm::mat4x4 GlmLookAt = glm::lookAtRH(glm::vec3(0,0,100),glm::vec3(0,0,0),glm::vec3(100,0,0));
      SMatrix4x4 LookAt=SMatrix4x4::LookAt(SVec3(0,0,100),SVec3(0,0,0)-SVec3(0,0,100),SVec3(100,0,0));
      WH::PrintLine(ToString(GlmLookAt));
      WH::PrintLine(LookAt.ToString());

      WH::PrintLine(U"Test Inverse mattrix 3x3");
      {
        glm::mat3x3 GlmInverse=glm::inverse(GlmMatrix3);
        SMatrix3x3 Inverse=Matrix3.Inverse();
        WH::PrintLine(ToString(GlmInverse));
        WH::PrintLine(Inverse.ToString());
      }

      WH::PrintLine(U"Test Inverse mattrix 4x4");
      {
        glm::mat4x4 GlmInverse=glm::inverse(GlmMatrix4);
        SMatrix4x4 Inverse=Matrix4.Inverse();
        WH::PrintLine(ToString(GlmInverse));
        WH::PrintLine(Inverse.ToString());
      }
      
      WH::PrintLine(U"Test Transpose mattrix 3x3");
      {
        glm::mat3x3 GlmTranspose=glm::transpose(GlmMatrix3);
        SMatrix3x3 Transpose=Matrix3.Transpose();
        WH::PrintLine(ToString(GlmTranspose));
        WH::PrintLine(Transpose.ToString());
      }


      WH::PrintLine(U"Test Transpose mattrix 4x4");
      {
        glm::mat4x4 GlmTranspose=glm::transpose(GlmMatrix4);
        SMatrix4x4 Transpose=Matrix4.Transpose();
        WH::PrintLine(ToString(GlmTranspose));
        WH::PrintLine(Transpose.ToString());
      }
   }

  TestPath();

  TestBasic();

   return App->Run();
 }